home *** CD-ROM | disk | FTP | other *** search
/ AI Game Programming Wisdom / AIGameProgrammingWisdom.iso / SourceCode / 11 Learning / 09 Laramée / Genetic.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-08-24  |  6.9 KB  |  207 lines

  1. /****************************************************************
  2.  * Implementation of Class Genetic
  3.  ***************************************************************/
  4.  
  5. #include "Genetic.h"
  6. #include "Simulation.h"
  7. #include "Troll.h"
  8. #include <stdlib.h>
  9. #include <iostream.h>
  10. #include <iomanip.h>
  11.  
  12.  
  13. /*****************************************************************
  14.  * Populating the world with trolls...
  15.  ****************************************************************/
  16.  
  17. void Genetic::MakeRandomIndividual( int which )
  18. {
  19.     Population[ which ].DNA.Priorities[ EATING_PRIORITY ] = (double) ( rand() % 10000 ) / 10000.0;
  20.     Population[ which ].DNA.Priorities[ KILLING_PRIORITY ] = (double) ( rand() % 10000 ) / 10000.0;
  21.     Population[ which ].DNA.Priorities[ FLEEING_PRIORITY ] = (double) ( rand() % 10000 ) / 10000.0;
  22.     Population[ which ].DNA.Priorities[ EXPLORING_PRIORITY ] = (double) ( rand() % 10000 ) / 10000.0;
  23.     Population[ which ].DNA.Priorities[ HEALING_PRIORITY ] = (double) ( rand() % 10000 ) / 10000.0;
  24. }
  25.  
  26. void Genetic::CreateInitialPopulation()
  27. {
  28.     for( int i = 0; i < GA_POPULATION_SIZE; i++ )
  29.     {
  30.         MakeRandomIndividual( i );
  31.     }
  32. }
  33.  
  34. /*****************************************************************
  35.  * The evolutionary process
  36.  ****************************************************************/
  37.  
  38. // The mothership method for the whole process
  39. void Genetic::RunEvolution()
  40. {
  41.     // Set up the gene pool
  42.     CreateInitialPopulation();
  43.     theSim.BuildTestCases();
  44.  
  45.     // And iterate the testing process over N generations
  46.     for( int i = 0; i < GA_GENERATIONS; i++ )
  47.     {
  48.         RunGeneration();
  49.         ReportGenerationResults( i );
  50.         if( i < GA_GENERATIONS - 1 )
  51.             BegetNextGeneration();
  52.     }
  53. }
  54.  
  55. // Helper function to classify individuals based on performance
  56. int RankIndividuals( const void * a, const void * b )
  57. {
  58.     Individual * ia = ( Individual * ) a;
  59.     Individual * ib = ( Individual * ) b;
  60.  
  61.     if( ia->Performance > ib->Performance )
  62.         return -1;
  63.     else
  64.         return 1;
  65. }
  66.  
  67. // Test an entire population of trolls
  68. void Genetic::RunGeneration()
  69. {
  70.     for( int i = 0; i < GA_POPULATION_SIZE; i++ )
  71.     {
  72.         // Place the troll in the game world
  73.         Troll theTroll( Population[ i ].DNA, rand() % WORLD_GRID_SIZE, rand() % WORLD_GRID_SIZE );
  74.         Entity::AttachTroll( theTroll );
  75.  
  76.         // Run the simulation with this troll and extract the results
  77.         Population[ i ].Performance = theSim.RunSim( theTroll );
  78.         Population[ i ].StatsSheepEaten = theTroll.StatsSheepEaten;
  79.         Population[ i ].StatsKnightsKilled = theTroll.StatsKnightsKilled;
  80.         Population[ i ].StatsDamageTaken = theTroll.StatsDamageTaken;
  81.         Population[ i ].StatsTimeAlive = theTroll.StatsTimeAlive;
  82.         Population[ i ].StatsTimeCaptive = theTroll.StatsTimeCaptive;
  83.     }
  84.  
  85.     // And classify the population
  86.     qsort( Population, GA_POPULATION_SIZE, sizeof( Individual ), RankIndividuals );
  87. }
  88.  
  89.  
  90. // How do we generate a new population according to the results of
  91. // their parents?
  92. // WARNING: THIS FUNCTION WILL NOT WORK IF POPULATION SIZE IS LESS 
  93. // THAN 90 INDIVIDUALS.  For smaller populations, you will have to
  94. // change the parenting rules.
  95. void Genetic::BegetNextGeneration()
  96. {
  97.     // First, we keep the top 20 individuals from the last 
  98.     // generation, without change.
  99.  
  100.     // Then, we make 42 new individuals by mating the top
  101.     // 7 performers, giving the higher-ranked individuals more
  102.     // chances to reproduce.  The following chunk of code only
  103.     // selects the parents; mating occurs later.
  104.     int currentIndex = 20;
  105.     for( int higher = 0; higher < 6; higher++ )
  106.     {
  107.         for( int lower = higher + 1; lower < 7; lower++ )
  108.         {
  109.             Population[ currentIndex++ ] = Population[ higher ];
  110.             Population[ currentIndex++ ] = Population[ lower ];
  111.         }
  112.     }
  113.  
  114.     // Then, we mate 14 pairs of randomly-selected individuals
  115.     // from the top third of the parent population.  Again, we
  116.     // only select the parents here; mating for both this group
  117.     // and the preceding one takes place later
  118.     for( int i = 0; i < 14; i++ )
  119.     {
  120.         Population[ currentIndex++ ] = Population[ rand() % 10 ];
  121.         Population[ currentIndex++ ] = Population[ rand() % 25 + 10 ];
  122.     }
  123.  
  124.     // This is the actual mating process for the last two groups
  125.     for( i = 20; i < 90; i += 2 )
  126.     {
  127.         // First, apply crossover to each pair of parents
  128.         Crossover( Population[ i ].DNA, Population[ i + 1 ].DNA );
  129.  
  130.         // And then mutate the children, maybe
  131.         Mutation( Population[ i ].DNA );
  132.         Mutation( Population[ i + 1 ].DNA );
  133.     }
  134.  
  135.     // Finally, complete the population with a handful of brand
  136.     // new individuals to introduce new variety into the gene pool
  137.     for( i = 90; i < GA_POPULATION_SIZE; i++ )
  138.     {
  139.         MakeRandomIndividual( i );
  140.     }
  141. }
  142.  
  143.  
  144. /****************************************************************
  145.  * The Genetic Operators
  146.  ***************************************************************/
  147.  
  148.  
  149. // We crossover using the Disruption method (each gene can
  150. // crossover independantly from the others).  Given the small
  151. // number of genes in our scenario, this sounds like a reasonable
  152. // approach.
  153. void Genetic::Crossover( Chromosome & c1, Chromosome & c2 )
  154. {
  155.     for( int i = 0; i < ALL_PRIORITIES; i++ )
  156.     {
  157.         if( rand() % 2 == 0 )
  158.         {
  159.             double tmp = c1.Priorities[ i ];
  160.             c1.Priorities[ i ] = c2.Priorities[ i ];
  161.             c2.Priorities[ i ] = tmp;
  162.         }
  163.     }
  164. }
  165.  
  166. // We mutate by replacing one gene by a random number, once
  167. // in a while
  168. void Genetic::Mutation( Chromosome & c )
  169. {
  170.     for( int i = 0; i < ALL_PRIORITIES; i++ )
  171.     {
  172.         if( rand() % 100 < MUTATION_RATE )
  173.         {
  174.             c.Priorities[ i ] = (double) (rand() % 10000) / 10000.0;
  175.         }
  176.     }
  177. }
  178.  
  179.  
  180. /****************************************************************
  181.  * REPORTING RESULTS
  182.  ***************************************************************/
  183.  
  184. void Genetic::ReportGenerationResults( int which )
  185. {
  186.     cout << "RESULTS FOR GENERATION " << which << endl;
  187.     cout << "-------------------------" << endl << endl;
  188.  
  189.     cout << "   EAT   KILL   HEAL   FLEE   EXPL    SHEEP  KNIGHT  ALIVE  CAPTV  DAMAG    PERF" << endl;
  190.     for( int i = 0; i < GA_POPULATION_SIZE; i++ )
  191.     {
  192.         cout << setw( 6 ) << setprecision( 5 ) << Population[ i ].DNA.Priorities[ EATING_PRIORITY ] << " "
  193.                  << setw( 6 ) << setprecision( 5 ) << Population[ i ].DNA.Priorities[ KILLING_PRIORITY ] << " "
  194.                  << setw( 6 ) << setprecision( 5 ) << Population[ i ].DNA.Priorities[ HEALING_PRIORITY ] << " "
  195.                  << setw( 6 ) << setprecision( 5 ) << Population[ i ].DNA.Priorities[ FLEEING_PRIORITY ] << " "
  196.                  << setw( 6 ) << setprecision( 5 ) << Population[ i ].DNA.Priorities[ EXPLORING_PRIORITY ] << " "
  197.                  << setw( 8 ) << Population[ i ].StatsSheepEaten << " "
  198.                  << setw( 7 ) << Population[ i ].StatsKnightsKilled << " "
  199.                  << setw( 6 ) << Population[ i ].StatsTimeAlive << " "
  200.                  << setw( 6 ) << Population[ i ].StatsTimeCaptive << " "
  201.                  << setw( 6 ) << Population[ i ].StatsDamageTaken << " "
  202.                  << setw( 8 ) << setprecision( 6 ) << Population[ i ].Performance
  203.                  << endl;
  204.     }
  205.  
  206.     cout << endl << endl;
  207. }